package ua.edu.nuos.jeetraining2012.cms.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ua.edu.nuos.jeetraining2012.cms.entity.CourseEntity;
import ua.edu.nuos.jeetraining2012.cms.entity.UserEntity;
import ua.edu.nuos.jeetraining2012.cms.entity.UsersToCourseEntity;
import ua.edu.nuos.jeetraining2012.cms.entity.UsersToCourseId;
import ua.edu.nuos.jeetraining2012.cms.model.StudentsToCoursesDAO;
import ua.edu.nuos.jeetraining2012.cms.model.exception.DAOException;
import ua.edu.nuos.jeetraining2012.cms.service.exception.ServiceException;
import ua.edu.nuos.jeetraining2012.cms.util.svn.RepositoryProperties;
import ua.edu.nuos.jeetraining2012.cms.util.svn.SvnRepository;
import ua.edu.nuos.jeetraining2012.cms.util.svn.SvnServeConf;
import ua.edu.nuos.jeetraining2012.cms.util.svn.Transliterator;
import ua.edu.nuos.jeetraining2012.cms.util.svn.exception.SvnException;

import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Stateless;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: artur
 * Date: 29.06.13
 * Time: 6:42
 * To change this template use File | Settings | File Templates.
 */
@Stateless
@Local
public class Svn {
    /**
     * Define logger.
     */
    final static private Logger logger = LoggerFactory.getLogger(Svn.class);

    @SuppressWarnings({"EjbEnvironmentInspection"})
    @EJB
    private Course course;

    @SuppressWarnings({"EjbEnvironmentInspection"})
    @EJB
    private User user;

    @SuppressWarnings({"EjbEnvironmentInspection"})
    @EJB
    private StudentsToCoursesDAO studentsToCoursesDAO;


    SvnServeConf conf = new SvnServeConf();
    SvnRepository repository = new SvnRepository();

    /**
     * Check that isExist SVN root directory, that must consist all repositories, and create it, if necessary;
     * create in this directory repository that match Course;
     * change default authorization settings for created repository.
     * Vit0r:
     * In case of success, just created repository's name will be returned.
     *
     * @param courseEntity CourseEntity must be fill
     * @return created repository name.
     * @throws ServiceException
     */
    public String createRepository(CourseEntity courseEntity) throws ServiceException {
        logger.debug("Method createRepository - invocation", courseEntity);

        if (courseEntity == null) {
            logger.warn("CourseEntity is null");
            throw new IllegalArgumentException("CourseEntity can't be null");
        }

        String repositoryName = createNameRepository(courseEntity);

        try {
            if (!repository.isExistRepository(repositoryName)) {
                repository.createRepository(repositoryName);
            }
            conf.changeDefaultSettings(repositoryName);

        } catch (SvnException | IOException e) {
            logger.warn("createRepository() - fin ish with SvnException. \n" +
                    "Can't create Svn repository. Error: {}", e.getMessage());
            throw new ServiceException("Can't create Svn repository. Error: " + e.getMessage());
        }
        return repositoryName;
    }

    /**
     * Method Update rights for all users that was enrolled to any Course(s)
     *
     * @throws ServiceException
     */
    public void updateUserRights() throws ServiceException {
        List<CourseEntity> availableCourses = course.getAvailableCourses();
        List<UserEntity> availableUsers = user.getAvailableUsers();
        Map<String, String> rights;

        for (CourseEntity currentCourse : availableCourses) {
            rights = new HashMap<>();

            for (UserEntity currentUser : availableUsers) {
                if (course.isEnrolledToThisCourse(currentUser.getId(), currentCourse.getId())) {
                    rights.put(getUserFolderName(currentUser.getEmail()), currentUser.getSvnPassword());
                }
            }

            if (!rights.isEmpty()) {
                if (!conf.writeAuthz(createNameRepository(currentCourse), rights))
                    throw new ServiceException("updateUserRights() - finish with ServiceException.\n" +
                            "Cant write user Authorization data");
            }
        }
    }

    /**
     * Method return User's SVN Link if it is exist already,
     * otherwise link will generated.
     *
     * @param courseEntity
     * @param userEntity
     * @return String User's SVN Link
     * @throws ServiceException
     */
    public String getUserUrl(CourseEntity courseEntity, UserEntity userEntity) throws ServiceException {
        logger.debug("Method getUserUrl - invocation");

        if (userEntity == null) {
            logger.warn("UserEntity is null");
            throw new IllegalArgumentException("UserEntity can't be null");
        }

        if (courseEntity == null) {
            logger.warn("CourseEntity is null");
            throw new IllegalArgumentException("CourseEntity can't be null");
        }

        String link;
        UsersToCourseId usersToCourseId = new UsersToCourseId();
        usersToCourseId.setCourseId(courseEntity.getId());
        usersToCourseId.setUserId(userEntity.getId());

        boolean isEnrolled = course.isEnrolledToThisCourse(userEntity.getId(), courseEntity.getId());
        if (isEnrolled) {
            try {
                if ((link = isExistUserSvnLink(usersToCourseId)) != null) {
                    return link;
                } else {
                    String repositoryName = createNameRepository(courseEntity);
                    String folder = getUserFolderName(userEntity.getEmail());

                    if (repository.isExistRepository(repositoryName)) {
                        link = getSvnLink(repositoryName, folder);
                    } else {
                        logger.warn("Svn repository not found at {}", createNameRepository(courseEntity));
                        throw new SvnException("Svn repository not found at " + createNameRepository(courseEntity));
                    }
                }

            } catch (IOException e) {
                logger.debug("Method getUserUrl finish with IOException - {}" + e.getMessage());
                throw new ServiceException("Method getUserUrl finish with IOException - " + e.getMessage());
            } catch (SvnException e) {
                logger.warn("Method getUserUrl finish with SvnException - {}", e.getMessage());
                throw new ServiceException("Method getUserUrl finish with SvnException - " + e.getMessage());
            }
            return link;
        } else {
            throw new ServiceException("User " + userEntity.getId() + " isn't enroll to " + courseEntity.getTitle() + " Course");
        }
    }

    /**
     * Method Check that is exist repository, and userDAO's folder for do commit in
     * Create repository or userDAO's root folder and return link to this folder
     *
     * @param repositoryName string Repository Name
     * @param folder         string User's root folder
     * @return string User's SVN link
     * @throws SvnException If it's impossible create link
     */
    private String getSvnLink(String repositoryName, String folder) throws SvnException {
        logger.debug("getSvnLink() - invocation");

        String link;
        if (repository.isExistFolderInRepository(repositoryName, folder)) {
            link = "svn://" + RepositoryProperties.getServerUrl() + "/" + repositoryName + "/" + folder;
        } else {
            link = repository.importUserFolder(repositoryName, folder);
        }

        logger.debug("getSvnLink() - finish with {}", link);
        return link;
    }

    /**
     * Method check that User Exist generated link of SVN to enrolled Course
     *
     * @param usersToCourseId UsersToCourseId
     * @return String Link if it Exist un UserToCourse already
     *         null otherwise
     * @throws SvnException if was thowed DAOException
     */
    private String isExistUserSvnLink(UsersToCourseId usersToCourseId) throws SvnException {
        logger.debug("isExistUserSvnLink() - invocation");
        UsersToCourseEntity usersToCourseEntity;

        try {
            usersToCourseEntity = studentsToCoursesDAO.findByUsersToCourseId(usersToCourseId);

            if (usersToCourseEntity != null) {
                if (usersToCourseEntity.getSvnLink() != null && !usersToCourseEntity.getSvnLink().isEmpty()) {
                    return usersToCourseEntity.getSvnLink();
                }
            }
        } catch (DAOException e) {
            logger.warn("DAOException", e.getMessage());
            throw new SvnException("isExistUserSvnLink() - finish with DAOException");
        }
        logger.debug("isExistUserSvnLink() - finish");
        return null;
    }


    /**
     * Get Login name from e-mail.
     * For example:  examplelogin@example.com method will return examplelogin.
     *
     * @param email User's E-mail
     * @return login name
     */
    private String getUserFolderName(String email) {
        return email.split("@")[0];
    }

    /**
     * Method create name of repository, that consist from course title and
     * less of dates: course start date or course finish date.
     * <p/>
     * If title consist from 1 word this word will use as part of repository name,
     * if title consist from more then 1 word, for name will use 1 First char of each word from title,
     * and default name "UnknownNameRepository" otherwise.
     * <p/>
     * For example:
     * Less Date = 2013;
     * 1. title = example                   will return example2013;
     * 2. title = first course example      will return fce2013;
     * 3. otherwise                         will return UnknownNameRepository.
     *
     * @param courseEntity Course Entity, must be fill.
     * @return created repository name
     */
    private String createNameRepository(CourseEntity courseEntity) {

        String titles[] = courseEntity.getTitle().split(" ");
        String title = "";

        if (titles.length == 1) {
            title = titles[0];
            title = Transliterator.transliterate(title) + compareDate(courseEntity.getStartDate(), courseEntity.getFinishDate());
        } else if (titles.length > 1) {
            for (String s : titles) {
                title = title.concat(s.substring(0, 1));
                title = Transliterator.transliterate(title);
            }
            title += compareDate(courseEntity.getStartDate(), courseEntity.getFinishDate());
        } else {
            title = "UnknownNameRepository";
        }

        return title;
    }

    /**
     * Method compare two dates (only Year) and return less of them.
     *
     * @param start  Date of begin course
     * @param finish Date of finish course
     * @return less date
     */
    private String compareDate(Date start, Date finish) {
        Date date = (start.before(finish)) ? start : finish;
        DateFormat df = new SimpleDateFormat("yyyy");
        return df.format(date);
    }
}
